/* Copyright (c) Microsoft Corporation.
   Licensed under the MIT License. */

/***************************************************************************
	Author: ShonK
	Project: Kauai
	Reviewed:
	Copyright (c) Microsoft Corporation

	Integer, rectangle and point utilities.
	WARNING: Must be in a fixed (pre-loaded) seg on Mac.

***************************************************************************/
#include "util.h"
ASSERTNAME


/***************************************************************************
	Calculates the GCD of two longs.
***************************************************************************/
long LwGcd(long lw1, long lw2)
{
	return LuGcd(LwAbs(lw1), LwAbs(lw2));
}


/***************************************************************************
	Calculates the GCD of two unsigned longs.
***************************************************************************/
ulong LuGcd(ulong lu1, ulong lu2)
{
	//Euclidean algorithm - keep mod'ing until we hit zero
	if (lu1 == 0)
		{
		// if both are zero, return 1.
		return lu2 == 0 ? 1 : lu2;
		}

	for (;;)
		{
		lu2 %= lu1;
		if (lu2 == 0)
			return lu1 == 0 ? 1 : lu1;
		lu1 %= lu2;
		if (lu1 == 0)
			return lu2;
		}
}


/***************************************************************************
	Sort the two longs so the smaller is in *plw1.
***************************************************************************/
void SortLw(long *plw1, long *plw2)
{
	if (*plw1 > *plw2)
		{
		long lwT = *plw1;
		*plw1 = *plw2;
		*plw2 = lwT;
		}
}


#ifndef MC_68020 //68020 version in utilmc.asm
#ifndef IN_80386 //80386 version inline in utilint.h
/***************************************************************************
	Multiply lw by lwMul and divide by lwDiv without losing precision.
***************************************************************************/
long LwMulDiv(long lw, long lwMul, long lwDiv)
{
	Assert(lwDiv != 0, "divide by zero error");
	double dou;

	dou = (double)lw * lwMul / lwDiv;
	Assert(dou <= klwMax && dou >= klwMin, "overflow in LwMulDiv");
	return (long)dou;
}


/***************************************************************************
	Return the quotient and set *plwRem to the remainder when (lw * lwMul)
	is divided by lwDiv.
***************************************************************************/
long LwMulDivMod(long lw, long lwMul, long lwDiv, long *plwRem)
{
	Assert(lwDiv != 0, "moding by 0");
	AssertVarMem(plwRem);
	double dou;

	dou = (double)lw * lwMul;
	*plwRem = (long)(dou % lwDiv);
	dou /= lwDiv;
	Assert(dou <= klwMax && dou >= klwMin, "overflow in LwMulDiv");
	return (long)dou;
}
#endif //IN_80386


/***************************************************************************
	Multiply two longs to get a 64 bit (signed) result.
***************************************************************************/
void MulLw(long lw1, long lw2, long *plwHigh, ulong *pluLow)
{
#ifdef IN_80386
	__asm
		{
		mov		eax,lw1
		imul	lw2
		mov		ebx,plwHigh
		mov		[ebx],edx
		mov		ebx,pluLow
		mov		[ebx],eax
		}
#else //!IN_80386
	double dou;
	bool fNeg;

	fNeg = 0 > (dou = (double)lw1 * lw2);
	if (fNeg)
		dou = -dou;
	*plwHigh = (long)(dou / ((double)0x10000 * 0x10000));
	*pluLow = dou - *plwHigh * ((double)0x10000 * 0x10000);
	if (fNeg)
		{
		if (*pluLow == 0)
            *plwHigh = -*plwHigh;
		else
			{
			*pluLow = -*pluLow;
            *plwHigh = ~*plwHigh;
			}
		}
#endif //!IN_80386
}


/***************************************************************************
	Multiply lu by luMul and divide by luDiv without losing precision.
***************************************************************************/
ulong LuMulDiv(ulong lu, ulong luMul, ulong luDiv)
{
	Assert(luDiv != 0, "divide by zero error");

#ifdef IN_80386
	//REVIEW shonk: this will fault on overflow!
	__asm
		{
		mov		eax,lu
		mul		luMul
		div		luDiv
		mov		lu,eax
		}
	return lu;
#else //!IN_80386
	double dou;

	dou = (double)lu * luMul / luDiv;
	Assert(dou <= kluMax && dou >= 0, "overflow in LuMulDiv");
	return (ulong)dou;
#endif //!IN_80386
}


/***************************************************************************
	Multiply two unsigned longs to get a 64 bit (unsigned) result.
***************************************************************************/
void MulLu(ulong lu1, ulong lu2, ulong *pluHigh, ulong *pluLow)
{
#ifdef IN_80386
	__asm
		{
		mov		eax,lu1
		mul		lu2
		mov		ebx,pluHigh
		mov		[ebx],edx
		mov		ebx,pluLow
		mov		[ebx],eax
		}
#else //!IN_80386
	double dou;

	dou = (double)lu1 * lu2;
	*pluHigh = (ulong)(dou / ((double)0x10000 * 0x10000));
	*pluLow = dou - *pluHigh * ((double)0x10000 * 0x10000);
#endif //!IN_80386
}
#endif //!MC_68020


/***************************************************************************
	Does a multiply and divide without losing precision, rounding away from
	zero during the divide.
***************************************************************************/
long LwMulDivAway(long lw, long lwMul, long lwDiv)
{
	Assert(lwDiv != 0, "divide by zero error");
	long lwT, lwRem;

	lwT = LwMulDivMod(lw, lwMul, lwDiv, &lwRem);
	if (lwRem != 0)
		{
		//divide wasn't exact
		if (lwT < 0)
			lwT--;
		else
			lwT++;
		}

	return lwT;
}


/***************************************************************************
	Does a multiply and divide without losing precision, rounding away from
	zero during the divide.
***************************************************************************/
ulong LuMulDivAway(ulong lu, ulong luMul, ulong luDiv)
{
	Assert(luDiv != 0, "divide by zero error");
	ulong luT;

	//get rid of common factors
	if (1 < (luT = LuGcd(lu, luDiv)))
		{
		lu /= luT;
		luDiv /= luT;
		}
	if (1 < (luT = LuGcd(luMul, luDiv)))
		{
		luMul /= luT;
		luDiv /= luT;
		}

	return LuMulDiv(lu, luMul, luDiv) + (luDiv > 1);
}


/***************************************************************************
	Returns lwNum divided by lwDen rounded away from zero.
***************************************************************************/
long LwDivAway(long lwNum, long lwDen)
{
	Assert(lwDen != 0, "divide by zero");

	//make sure lwDen is greater than zero
	if (lwDen < 0)
		{
		lwDen = -lwDen;
		lwNum = -lwNum;
		}
	if (lwNum < 0)
		lwNum -= (lwDen - 1);
	else
		lwNum += (lwDen - 1);
	return lwNum / lwDen;
}


/***************************************************************************
	Returns lwNum divided by lwDen rounded toward the closest integer.
***************************************************************************/
long LwDivClosest(long lwNum, long lwDen)
{
	Assert(lwDen != 0, "divide by zero");

	//make sure lwDen is greater than zero
	if (lwDen < 0)
		{
		lwDen = -lwDen;
		lwNum = -lwNum;
		}
	if (lwNum < 0)
		lwNum -= lwDen / 2;
	else
		lwNum += lwDen / 2;
	return lwNum / lwDen;
}


/***************************************************************************
	Rounds lwSrc to a multiple of lwBase.  The rounding is done away from
	zero.  Equivalent to LwDivAway(lwSrc, lwBase) * lwBase.
***************************************************************************/
long LwRoundAway(long lwSrc, long lwBase)
{
	Assert(lwBase != 0, "divide by zero");

	//make sure lwBase is greater than zero
	if (lwBase < 0)
		lwBase = -lwBase;
	if (lwSrc < 0)
		lwSrc -= (lwBase - 1);
	else
		lwSrc += (lwBase - 1);
	return lwSrc - (lwSrc % lwBase);
}


/***************************************************************************
	Rounds lwSrc to a multiple of lwBase.  The rounding is done toward zero.
	Equivalent to (lwSrc / lwBase) * lwBase.
***************************************************************************/
long LwRoundToward(long lwSrc, long lwBase)
{
	Assert(lwBase != 0, "divide by zero");

	//make sure lwBase is greater than zero
	if (lwBase < 0)
		lwBase = -lwBase;
	return lwSrc - (lwSrc % lwBase);
}


/***************************************************************************
	Rounds lwSrc to the closest multiple of lwBase.
	Equivalent to LwDivClosest(lwSrc, lwBase) * lwBase.
***************************************************************************/
long LwRoundClosest(long lwSrc, long lwBase)
{
	Assert(lwBase != 0, "divide by zero");

	//make sure lwBase is greater than zero
	if (lwBase < 0)
		lwBase = -lwBase;
	if (lwSrc < 0)
		lwSrc -= lwBase / 2;
	else
		lwSrc += lwBase / 2;
	return lwSrc - (lwSrc % lwBase);
}


/***************************************************************************
	Returns fcmpGt, fcmpEq or fcmpLt according to whether (lwNum1 / lwDen2)
	is greater than, equal to or less than (lwNum2 / lwDen2).
***************************************************************************/
ulong FcmpCompareFracs(long lwNum1, long lwDen1, long lwNum2, long lwDen2)
{
	long lwHigh1, lwHigh2;  //must be signed
	ulong luLow1, luLow2;   //must be unsigned

	MulLw(lwNum1, lwDen2, &lwHigh1, &luLow1);
	MulLw(lwNum2, lwDen1, &lwHigh2, &luLow2);

	if (lwHigh1 > lwHigh2)
		return fcmpGt;
	if (lwHigh1 < lwHigh2)
		return fcmpLt;

	// the high 32 bits are the same, so just compare the low 32 bits
	// (as unsigned longs)
	Assert(lwHigh1 == lwHigh2, 0);
	if (luLow1 > luLow2)
		return fcmpGt;
	if (luLow1 < luLow2)
		return fcmpLt;
	return fcmpEq;
}


/***************************************************************************
	Adjusts an index after an edit.  *piv is the index to adjust, iv is
	the index where the edit occurred, cvIns is the number of things inserted
	and cvDel is the number deleted.  Returns true iff *piv is not in
	(iv, iv + cvDel).  If *piv is in this interval, mins *piv with iv + cvIns
	and returns false.
***************************************************************************/
bool FAdjustIv(long *piv, long iv, long cvIns, long cvDel)
{
	AssertVarMem(piv);
	AssertIn(iv, 0, kcbMax);
	AssertIn(cvIns, 0, kcbMax);
	AssertIn(cvDel, 0, kcbMax);

	if (*piv <= iv)
		return fTrue;
	if (*piv < iv + cvDel)
		{
		*piv = LwMin(*piv, iv + cvIns);
		return fFalse;
		}
	*piv += cvIns - cvDel;
	return fTrue;
}


#ifdef DEBUG
/***************************************************************************
	Multiplies two longs.  Asserts on overflow.
***************************************************************************/
long LwMul(long lw1, long lw2)
{
	if (lw1 == 0)
		return 0;
	Assert((lw1 * lw2) / lw1 == lw2, "overflow");
	return lw1 * lw2;
}


/***************************************************************************
	Asserts that the lw is >= lwMin and < lwLim.
***************************************************************************/
void AssertIn(long lw, long lwMin, long lwLim)
{
	Assert(lw >= lwMin, "long too small");
	Assert(lw < lwLim, "long too big");
}
#endif //DEBUG


/***************************************************************************
	Swap bytes in pv according to bom.  bom consists of up to 16
	2-bit opcodes (packed from hi bit to low bit).  The high bit of
	each opcode indicates a long field (1) or short field (0).  The low
	bit of each opcode indicates whether the bytes are to be swapped
	in the field (1) or left alone (0).
***************************************************************************/
void SwapBytesBom(void *pv, BOM bom)
{
	byte b;
	byte *pb = (byte *)pv;

	Assert(size(short) == 2 && size(long) == 4, "code broken");
	while (bom != 0)
		{
		if (bom & 0x80000000L)
			{
			// long field
			AssertPvCb(pb, 4);
			if (bom & 0x40000000L)
				{
				b = pb[3];
				pb[3] = pb[0];
				pb[0] = b;
				b = pb[2];
				pb[2] = pb[1];
				pb[1] = b;
				}
			pb += 4;
			}
		else
			{
			// short field
			AssertPvCb(pb, 2);
			if (bom & 0x40000000L)
				{
				b = pb[1];
				pb[1] = pb[0];
				pb[0] = b;
				}
			pb += 2;
			}
		bom <<= 2;
		}
}


/***************************************************************************
	Swap bytes within an array of short words.
***************************************************************************/
void SwapBytesRgsw(void *psw, long csw)
{
	AssertIn(csw, 0, kcbMax);
	AssertPvCb(psw, LwMul(csw, size(short)));

	byte b;
	byte *pb = (byte *)psw;

	Assert(size(short) == 2, "code broken");
	for ( ; csw > 0; csw--, pb += 2)
		{
		b = pb[1];
		pb[1] = pb[0];
		pb[0] = b;
		}
}


/***************************************************************************
	Swap bytes within an array of long words.
***************************************************************************/
void SwapBytesRglw(void *plw, long clw)
{
	AssertIn(clw, 0, kcbMax);
	AssertPvCb(plw, LwMul(clw, size(long)));

	byte b;
	byte *pb = (byte *)plw;

	Assert(size(long) == 4, "code broken");
	for ( ; clw > 0; clw--, pb += 4)
		{
		b = pb[3];
		pb[3] = pb[0];
		pb[0] = b;
		b = pb[2];
		pb[2] = pb[1];
		pb[1] = b;
		}
}


#ifdef DEBUG
/***************************************************************************
	Asserts that the given BOM indicates a struct having cb/size(long) longs
	to be swapped (so SwapBytesRglw can legally be used on an array of
	these).
***************************************************************************/
void AssertBomRglw(BOM bom, long cb)
{
	BOM bomT;
	long clw;

	clw = cb / size(long);
	Assert(cb == clw * size(long), "cb is not a multiple of size(long)");
	AssertIn(clw, 1, 17);
	bomT = -1L << 2 * (16 - clw);
	Assert(bomT == bom, "wrong bom");
}


/***************************************************************************
	Asserts that the given BOM indicates a struct having cb/size(short) shorts
	to be swapped (so SwapBytesRgsw can legally be used on an array of
	these).
***************************************************************************/
void AssertBomRgsw(BOM bom, long cb)
{
	BOM bomT;
	long csw;

	csw = cb / size(short);
	Assert(cb == csw * size(short), "cb is not a multiple of size(short)");
	AssertIn(csw, 1, 17);
	bomT = 0x55555555 << 2 * (16 - csw);
	Assert(bomT == bom, "wrong bom");
}
#endif //DEBUG


/***************************************************************************
	Truncates a util point to a system point.
	REVIEW shonk: should we assert on truncation?  Should we truncate
	on windows?
***************************************************************************/
PT::operator PTS(void)
{
	AssertThisMem();
	PTS pts;

	MacWin(pts.h, pts.x) = SwTruncLw(xp);
	MacWin(pts.v, pts.y) = SwTruncLw(yp);
	return pts;
}


/***************************************************************************
	Copies a system point to a util point.
***************************************************************************/
PT & PT::operator = (PTS &pts)
{
	AssertThisMem();
	xp = MacWin(pts.h, pts.x);
	yp = MacWin(pts.v, pts.y);
	return *this;
}


/***************************************************************************
	Map a point from prcSrc coordinates to prcDst coordinates.
***************************************************************************/
void PT::Map(RC *prcSrc, RC *prcDst)
{
	AssertThisMem();
	AssertVarMem(prcSrc);
	AssertVarMem(prcDst);
	long dzpSrc, dzpDst;

	if ((dzpDst = prcDst->Dxp()) == (dzpSrc = prcSrc->Dxp()))
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		xp += prcDst->xpLeft - prcSrc->xpLeft;
		}
	else
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		xp = prcDst->xpLeft + LwMulDiv(xp - prcSrc->xpLeft,
			dzpDst, dzpSrc);
		}
	if ((dzpDst = prcDst->Dyp()) == (dzpSrc = prcSrc->Dyp()))
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		yp += prcDst->ypTop - prcSrc->ypTop;
		}
	else
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		yp = prcDst->ypTop + LwMulDiv(yp - prcSrc->ypTop,
			dzpDst, dzpSrc);
		}
}


/***************************************************************************
	Map a point from prcSrc coordinates to prcDst coordinates.
***************************************************************************/
PT PT::PtMap(RC *prcSrc, RC *prcDst)
{
	AssertThisMem();
	PT pt = *this;
	pt.Map(prcSrc, prcDst);
	return pt;
}


/***************************************************************************
	Transform the xp and yp values according to grfpt.  Negating comes
	before transposition.
***************************************************************************/
void PT::Transform(ulong grfpt)
{
	AssertThisMem();
	long zp;

	if (grfpt & fptNegateXp)
		xp = -xp;
	if (grfpt & fptNegateYp)
		yp = -yp;
	if (grfpt & fptTranspose)
		{
		zp = xp;
		xp = yp;
		yp = zp;
		}
}


/***************************************************************************
	Check for equality, special casing empty.
***************************************************************************/
bool RC::operator==(RC &rc)
{
	AssertThisMem();

	if (FEmpty())
		return rc.FEmpty();

	return xpLeft == rc.xpLeft && ypTop == rc.ypTop &&
		xpRight == rc.xpRight && ypBottom == rc.ypBottom;
}


/***************************************************************************
	Check for non-equality, special casing empty.
***************************************************************************/
bool RC::operator!=(RC &rc)
{
	AssertThisMem();

	if (FEmpty())
		return !rc.FEmpty();

	return xpLeft != rc.xpLeft || ypTop != rc.ypTop ||
		xpRight != rc.xpRight || ypBottom != rc.ypBottom;
}


/***************************************************************************
	Unionize the rects.
***************************************************************************/
void RC::Union(RC *prc1, RC *prc2)
{
	AssertThisMem();
	AssertVarMem(prc1);
	AssertVarMem(prc2);

	*this = *prc1;
	Union(prc2);
}


/***************************************************************************
	Unionize the rects.
***************************************************************************/
void RC::Union(RC *prc)
{
	AssertThisMem();
	AssertVarMem(prc);

	// if a rect is empty, it shouldn't contribute to the union
	if (!prc->FEmpty())
		{
		if (FEmpty())
			*this = *prc;
		else
			{
			xpLeft = LwMin(xpLeft, prc->xpLeft);
			xpRight = LwMax(xpRight, prc->xpRight);
			ypTop = LwMin(ypTop, prc->ypTop);
			ypBottom = LwMax(ypBottom, prc->ypBottom);
			}
		}
}


/***************************************************************************
	Intersect the rects and return whether the result is non-empty.
***************************************************************************/
bool RC::FIntersect(RC *prc1, RC *prc2)
{
	AssertThisMem();
	AssertVarMem(prc1);
	AssertVarMem(prc2);

	xpLeft = LwMax(prc1->xpLeft, prc2->xpLeft);
	xpRight = LwMin(prc1->xpRight, prc2->xpRight);
	ypTop = LwMax(prc1->ypTop, prc2->ypTop);
	ypBottom = LwMin(prc1->ypBottom, prc2->ypBottom);

	if (FEmpty())
		{
		Zero();
		return fFalse;
		}
	return fTrue;
}


/***************************************************************************
	Intersect this rect with the given rect and return whether the result
	is non-empty.
***************************************************************************/
bool RC::FIntersect(RC *prc)
{
	AssertThisMem();
	AssertVarMem(prc);

	xpLeft = LwMax(xpLeft, prc->xpLeft);
	xpRight = LwMin(xpRight, prc->xpRight);
	ypTop = LwMax(ypTop, prc->ypTop);
	ypBottom = LwMin(ypBottom, prc->ypBottom);

	if (FEmpty())
		{
		Zero();
		return fFalse;
		}
	return fTrue;
}


/***************************************************************************
	Inset a rectangle (into another)
***************************************************************************/
void RC::InsetCopy(RC *prc, long dxp, long dyp)
{
	AssertThisMem();
	AssertVarMem(prc);

	xpLeft = prc->xpLeft + dxp;
	xpRight = prc->xpRight - dxp;
	ypTop = prc->ypTop + dyp;
	ypBottom = prc->ypBottom - dyp;
}


/***************************************************************************
	Inset a rectangle (in place)
***************************************************************************/
void RC::Inset(long dxp, long dyp)
{
	AssertThisMem();

	xpLeft += dxp;
	xpRight -= dxp;
	ypTop += dyp;
	ypBottom -= dyp;
}


/***************************************************************************
	Map this rectangle through the two rectangles (from prcSrc
	coordinates to prcDst coordinates).  This cannot be either prcSrc
	or prcDst.
***************************************************************************/
void RC::Map(RC *prcSrc, RC *prcDst)
{
	AssertThisMem();
	AssertVarMem(prcSrc);
	AssertVarMem(prcDst);
	long dzpSrc, dzpDst;

	if ((dzpDst = prcDst->Dxp()) == (dzpSrc = prcSrc->Dxp()))
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		xpLeft += prcDst->xpLeft - prcSrc->xpLeft;
		xpRight += prcDst->xpLeft - prcSrc->xpLeft;
		}
	else
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		xpLeft = prcDst->xpLeft + LwMulDiv(xpLeft - prcSrc->xpLeft,
			dzpDst, dzpSrc);
		xpRight = prcDst->xpLeft + LwMulDiv(xpRight - prcSrc->xpLeft,
			dzpDst, dzpSrc);
		}
	if ((dzpDst = prcDst->Dyp()) == (dzpSrc = prcSrc->Dyp()))
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		ypTop += prcDst->ypTop - prcSrc->ypTop;
		ypBottom += prcDst->ypTop - prcSrc->ypTop;
		}
	else
		{
		AssertVar(dzpSrc > 0, "empty map rectangle", prcSrc);
		ypTop = prcDst->ypTop + LwMulDiv(ypTop - prcSrc->ypTop,
			dzpDst, dzpSrc);
		ypBottom = prcDst->ypTop + LwMulDiv(ypBottom - prcSrc->ypTop,
			dzpDst, dzpSrc);
		}
}


/***************************************************************************
	Transform the xp and yp values according to grfpt.  Negating comes
	before transposition.
***************************************************************************/
void RC::Transform(ulong grfpt)
{
	AssertThisMem();
	long zp;

	if (grfpt & fptNegateXp)
		{
		zp = xpLeft;
		xpLeft = -xpRight;
		xpRight = -zp;
		}
	if (grfpt & fptNegateYp)
		{
		zp = ypTop;
		ypTop = -ypBottom;
		ypBottom = -zp;
		}
	if (grfpt & fptTranspose)
		{
		zp = xpLeft;
		xpLeft = ypTop;
		ypTop = zp;
		zp = xpRight;
		xpRight = ypBottom;
		ypBottom = zp;
		}
}


/***************************************************************************
	Move a rectangle (into another)
***************************************************************************/
void RC::OffsetCopy(RC *prc, long dxp, long dyp)
{
	AssertThisMem();
	AssertVarMem(prc);

	xpLeft = prc->xpLeft + dxp;
	xpRight = prc->xpRight + dxp;
	ypTop = prc->ypTop + dyp;
	ypBottom = prc->ypBottom + dyp;
}


/***************************************************************************
	Move a rectangle (in place)
***************************************************************************/
void RC::Offset(long dxp, long dyp)
{
	AssertThisMem();

	xpLeft += dxp;
	xpRight += dxp;
	ypTop += dyp;
	ypBottom += dyp;
}


/***************************************************************************
	Move the rectangle so the top left is (0, 0).
***************************************************************************/
void RC::OffsetToOrigin(void)
{
	AssertThisMem();

	xpRight -= xpLeft;
	ypBottom -= ypTop;
	xpLeft = ypTop = 0;
}


/***************************************************************************
	Move this rectangle so it is centered over *prcBase.
***************************************************************************/
void RC::CenterOnRc(RC *prcBase)
{
	AssertThisMem();
	AssertVarMem(prcBase);
	long dxp = Dxp();
	long dyp = Dyp();

	xpLeft = (prcBase->xpLeft + prcBase->xpRight - dxp) / 2;
	xpRight = xpLeft + dxp;
	ypTop = (prcBase->ypTop + prcBase->ypBottom - dyp) / 2;
	ypBottom = ypTop + dyp;
}


/***************************************************************************
	Centers this rectangle on (xp, yp).
***************************************************************************/
void RC::CenterOnPt(long xp, long yp)
{
	AssertThisMem();
	long dxp = Dxp();
	long dyp = Dyp();

	xpLeft = xp - dxp / 2;
	xpRight = xpLeft + dxp;
	ypTop = yp - dyp / 2;
	ypBottom = ypTop + dyp;
}


/***************************************************************************
	Center this rectangle over *prcBase.  If it doesn't fit inside *prcBase,
	scale it down so it does.
***************************************************************************/
void RC::SqueezeIntoRc(RC *prcBase)
{
	AssertThisMem();
	AssertVarMem(prcBase);

	if (Dxp() <= prcBase->Dxp() && Dyp() <= prcBase->Dyp())
		CenterOnRc(prcBase);
	else
		StretchToRc(prcBase);
}


/***************************************************************************
	Scale this rectangle proportionally (and translate it) so it is centered
	on *prcBase and as large as possible but still inside *prcBase.
***************************************************************************/
void RC::StretchToRc(RC *prcBase)
{
	AssertThisMem();
	AssertVarMem(prcBase);
	long dxp = Dxp();
	long dyp = Dyp();
	long dxpBase = prcBase->Dxp();
	long dypBase = prcBase->Dyp();

	if (dxp <= 0 || dyp <= 0)
		{
		Bug("empty rc to stretch");
		Zero();
		return;
		}

	if (FcmpCompareFracs(dxp, dyp, dxpBase, dypBase) & fcmpLt)
		{
		// height dominated
		dxp = LwMulDiv(dxp, dypBase, dyp);
		dyp = dypBase;
		}
	else
		{
		// width dominated
		dyp = LwMulDiv(dyp, dxpBase, dxp);
		dxp = dxpBase;
		}
	xpRight = xpLeft + dxp;
	ypBottom = ypTop + dyp;
	CenterOnRc(prcBase);
}


/***************************************************************************
	Determine if the given point is in the rectangle
***************************************************************************/
bool RC::FPtIn(long xp, long yp)
{
	AssertThisMem();

	return xp >= xpLeft && xp < xpRight &&
		yp >= ypTop && yp < ypBottom;
}


/***************************************************************************
	Pin the point to the rectangle.
***************************************************************************/
void RC::PinPt(PT *ppt)
{
	AssertThisMem();
	AssertVarMem(ppt);

	ppt->xp = LwBound(ppt->xp, xpLeft, xpRight);
	ppt->yp = LwBound(ppt->yp, ypTop, ypBottom);
}


/***************************************************************************
	Pin this rectangle to the given one.
***************************************************************************/
void RC::PinToRc(RC *prc)
{
	AssertThisMem();
	AssertVarMem(prc);
	long dxp, dyp;

	dxp = LwMax(LwMin(0, prc->xpRight - xpRight), prc->xpLeft - xpLeft);
	dyp = LwMax(LwMin(0, prc->ypBottom - ypBottom), prc->ypTop - ypTop);
	if (dxp != 0 || dyp != 0)
		Offset(dxp, dyp);
}


/***************************************************************************
	Copies a system rectangle to a util rectangle.
***************************************************************************/
RC & RC::operator = (RCS &rcs)
{
	AssertThisMem();

	xpLeft = (long)rcs.left;
	xpRight = (long)rcs.right;
	ypTop = (long)rcs.top;
	ypBottom = (long)rcs.bottom;
	return *this;
}


/***************************************************************************
	Truncates util rectangle to a system rectangle.
	REVIEW shonk: should we assert on truncation?
***************************************************************************/
RC::operator RCS(void)
{
	AssertThisMem();
	RCS rcs;

	rcs.left = SwTruncLw(xpLeft);
	rcs.right = SwTruncLw(xpRight);
	rcs.top = SwTruncLw(ypTop);
	rcs.bottom = SwTruncLw(ypBottom);
	return rcs;
}


/***************************************************************************
	Return the area of the rectangle.
***************************************************************************/
long RC::LwArea(void)
{
	AssertThisMem();

	if (FEmpty())
		return 0;
	return LwMul(xpRight - xpLeft, ypBottom - ypTop);
}


/***************************************************************************
	Return whether this rectangle fully contains *prc.
***************************************************************************/
bool RC::FContains(RC *prc)
{
	AssertThisMem();
	AssertVarMem(prc);

	return prc->FEmpty() ||
		prc->xpLeft >= xpLeft && prc->xpRight <= xpRight &&
		prc->ypTop >= ypTop && prc->ypBottom <= ypBottom;
}


/***************************************************************************
	Imagine *prcSrc divided into a crcWidth by crcHeight grid.  This sets
	this rc to the (ircWidth, ircHeight) cell of the grid.  prcSrc cannot
	be equal to this.
***************************************************************************/
void RC::SetToCell(RC *prcSrc, long crcWidth, long crcHeight,
	long ircWidth, long ircHeight)
{
	AssertThisMem();
	AssertVarMem(prcSrc);
	AssertIn(crcWidth, 1, kcbMax);
	AssertIn(crcHeight, 1, kcbMax);
	AssertIn(ircWidth, 0, crcWidth);
	AssertIn(ircHeight, 0, crcHeight);
	Assert(this != prcSrc, "this can't be prcSrc");

	xpLeft = prcSrc->xpLeft + LwMulDiv(prcSrc->Dxp(), ircWidth, crcWidth);
	xpRight = prcSrc->xpLeft + LwMulDiv(prcSrc->Dxp(), ircWidth + 1, crcWidth);
	ypTop = prcSrc->ypTop + LwMulDiv(prcSrc->Dyp(), ircHeight, crcHeight);
	ypBottom = prcSrc->ypTop + LwMulDiv(prcSrc->Dyp(), ircHeight + 1, crcHeight);
}


/***************************************************************************
	Determines which cell the given (xp, yp) is in.  This is essentially
	the inverse of SetToCell.
***************************************************************************/
bool RC::FMapToCell(long xp, long yp, long crcWidth, long crcHeight,
	long *pircWidth, long *pircHeight)
{
	AssertThisMem();
	AssertIn(crcWidth, 1, kcbMax);
	AssertIn(crcHeight, 1, kcbMax);
	AssertVarMem(pircWidth);
	AssertVarMem(pircHeight);

	if (!FPtIn(xp, yp))
		return fFalse;
	*pircWidth = LwMulDiv(xp - xpLeft, crcWidth, Dxp());
	*pircHeight = LwMulDiv(yp - ypTop, crcHeight, Dyp());
	return fTrue;
}


#ifdef DEBUG
/***************************************************************************
	Asserts the validity of a fraction.
***************************************************************************/
void RAT::AssertValid(ulong grf)
{
	AssertIn(_lwDen, 1, klwMax);
	long lwGcd = LwGcd(_lwNum, _lwDen);
	Assert(lwGcd == 1, "fraction not in lowest terms");
}
#endif //DEBUG


/***************************************************************************
	Constructor for the master clock.
***************************************************************************/
USAC::USAC(void)
{
	AssertThisMem();
	_tsBaseSys = MacWin(TickCount(), timeGetTime());
	_tsBaseApp = 0;
	_luScale = kluTimeScaleNormal;
}


/***************************************************************************
	Return the current application time.
***************************************************************************/
ulong USAC::TsCur(void)
{
	AssertThisMem();
	ulong dtsSys = TsCurrentSystem() - _tsBaseSys;

	if (_luScale != kluTimeScaleNormal)
		{
		ulong luHigh;
		MulLu(dtsSys, _luScale, &luHigh, &dtsSys);
		dtsSys = LwHighLow(SuLow(luHigh), SuHigh(dtsSys));
		}

	return _tsBaseApp + dtsSys;
}


/***************************************************************************
	Scale the time.
***************************************************************************/
void USAC::Scale(ulong luScale)
{
	AssertThisMem();
	ulong tsSys, dts;

	if (luScale == _luScale)
		return;

	// set the _tsBaseSys and _tsBaseApp to now and set _luScale to luScale.
	tsSys = TsCurrentSystem();
	dts = tsSys - _tsBaseSys;
	if (_luScale != kluTimeScaleNormal)
		{
		ulong luHigh;
		MulLu(dts, _luScale, &luHigh, &dts);
		dts = LwHighLow(SuLow(luHigh), SuHigh(dts));
		}
	_tsBaseApp += dts;
	_tsBaseSys = tsSys;
	_luScale = luScale;
}


/***************************************************************************
	Set the DVER structure.
***************************************************************************/
void DVER::Set(short swCur, short swBack)
{
	_swBack = swBack;
	_swCur = swCur;
}


/***************************************************************************
	Determines if the DVER structure is compatible with (swCur and swMin).
	Asserts that 0 <= swMin <= swCur.
***************************************************************************/
bool DVER::FReadable(short swCur, short swMin)
{
	AssertIn(_swBack, 0, _swCur + 1);
	AssertIn(_swCur, 0, kswMax);
	AssertIn(swMin, 0, swCur + 1);
	AssertIn(swCur, 0, kswMax);
	return _swCur >= swMin && _swBack <= swCur;
}


